package DataStructures;

import java.util.*;

public class MyLinkedListWithoutHeader<T> implements Iterable<T> {
	public MyLinkedListWithoutHeader() {
		clear();
	}
	
	public void clear() {
		theSize = 0;
		modCount++;
		firstNode = null;
		lastNode = null;
	}
	public int size() {
		return theSize;
	}
	public boolean isEmpty() {
		return size() == 0;
	}
	public boolean add(T x) {
		add(size(), x);
		return true;
	}
	public void add(int idx, T x) {
		if(size() == 0) {
			Node<T> newNode = new Node<T>(x, null, null);
			firstNode = newNode;
			lastNode = newNode;
		} else if(idx == size()) {
			Node<T> newNode = new Node<T>(x, lastNode, null);
			lastNode.next = newNode;
			lastNode = newNode;
		} else
			addBefore(getNode(idx), x);
	}
	public T get(int idx) {
		return getNode(idx).data;
	}
	public T set(int idx, T newVal) {
		Node<T> node = getNode(idx);
		T oldVal = node.data;
		node.data = newVal;
		return oldVal;
	}
	public T remove(int idx) {
		return remove(getNode(idx));
	}
	public Iterator<T> iterator() {
		return new LinkedListIterator();
	}
	
	private void addBefore(Node<T> p, T x) {
		Node<T> tmpNode = new Node<T>(x, p.prev, p);
		if(p == firstNode) {
			firstNode = tmpNode;
			p.prev = tmpNode;
		} else {
			p.prev.next = tmpNode;
			p.prev = tmpNode;
		}
		
		theSize++;
		modCount++;
	}
	
	private T remove(Node<T> p) {
		if(p == firstNode) {
			p.next.prev = p.prev;
			firstNode = p.next;
		} else if(p == lastNode) {
			p.prev.next = p.next;
			lastNode = p.prev;
		} else {
			p.prev.next = p.next;
			p.next.prev = p.prev;
		}
		theSize++;
		modCount++;
		
		return p.data;
	}
	
	private Node<T> getNode(int idx) {
		Node<T> tmpNode = null;
		
		if(idx < size()/2) {
			tmpNode = firstNode;
			for(int i = 0; i < idx; i++)
				tmpNode = tmpNode.next;
		} else if(idx >= size()/2 && idx < size()) {
			tmpNode = lastNode;
			for(int i = size() - 1; i > idx; i--)
				tmpNode = tmpNode.prev;
		} else throw new IndexOutOfBoundsException();
		
		return tmpNode;
	}
	
	private int theSize;
	private int modCount = 0;
	private static class Node<T> {
		public Node(T d, Node<T> p, Node<T> n) {
			data = d;
			prev = p;
			next = n;
		}
		
		public T data;
		public Node<T> prev;
		public Node<T> next;
	}
	private Node<T> firstNode;
	private Node<T> lastNode;
	
	private class LinkedListIterator implements Iterator<T> {
		private Node<T> current = firstNode;
		private int expectedModCount = modCount;
		public boolean okToRemove = false;
		
		public boolean hasNext() {
			return current != null;
		}
		
		public T next() {
			if(modCount != expectedModCount)
				throw new ConcurrentModificationException();
			if(!hasNext()) 
				throw new NoSuchElementException();
			
			T nextItem = current.data;
			current = current.next;
			okToRemove = true;
			return nextItem;
		}
		
		public void remove() {
			if(modCount != expectedModCount)
				throw new ConcurrentModificationException();
			if(!okToRemove)
				throw new IllegalStateException();
			
			MyLinkedListWithoutHeader.this.remove(current.prev);
			okToRemove = false;
			expectedModCount++;
		}
	}
}
